﻿using Autofac.Builder;
using Autofac.Core;
using Autofac.Extras.DynamicProxy;
using Z.DynamicProxy;
using Z.Foundation.Core.DependencyInjection;
using Z.Module.Extensions;
using Z.Module.Modules;
using Z.Module.Modules.interfaces;

namespace Z.Foundation.Core.AutofacExtensions.Builder;

public static class ZRegistrationBuilderExtensions
{
    public static IRegistrationBuilder<
        TLimit,
        TActivatorData,
        TRegistrationStyle
    > ConfigureZConventions<TLimit, TActivatorData, TRegistrationStyle>(
        this IRegistrationBuilder<TLimit, TActivatorData, TRegistrationStyle> registrationBuilder,
        IModuleContainer moduleContainer,
        ServiceRegistrationActionList registrationActionList
    )
        where TActivatorData : ReflectionActivatorData
    {
        var serviceType = registrationBuilder
            .RegistrationData.Services.OfType<IServiceWithType>()
            .FirstOrDefault()
            ?.ServiceType;
        if (serviceType == null)
        {
            return registrationBuilder;
        }

        var implementationType = registrationBuilder.ActivatorData.ImplementationType;
        if (implementationType == null)
        {
            return registrationBuilder;
        }

        registrationBuilder = registrationBuilder.EnablePropertyInjection(
            moduleContainer,
            implementationType
        );
        registrationBuilder = registrationBuilder.InvokeRegistrationActions(
            registrationActionList,
            serviceType,
            implementationType
        );

        return registrationBuilder;
    }

    /// <summary>
    /// 属性注入
    /// </summary>
    /// <typeparam name="TLimit"></typeparam>
    /// <typeparam name="TActivatorData"></typeparam>
    /// <typeparam name="TRegistrationStyle"></typeparam>
    /// <param name="registrationBuilder"></param>
    /// <param name="moduleContainer"></param>
    /// <param name="implementationType"></param>
    /// <returns></returns>
    private static IRegistrationBuilder<
        TLimit,
        TActivatorData,
        TRegistrationStyle
    > EnablePropertyInjection<TLimit, TActivatorData, TRegistrationStyle>(
        this IRegistrationBuilder<TLimit, TActivatorData, TRegistrationStyle> registrationBuilder,
        IModuleContainer moduleContainer,
        Type implementationType
    )
        where TActivatorData : ReflectionActivatorData
    {
        // Enable Property Injection only for types in an assembly containing an AbpModule and without a DisablePropertyInjection attribute on class or properties.
        if (
            moduleContainer.Modules.Any(m => m.AllAssemblies.Contains(implementationType.Assembly))
            && implementationType
                .GetCustomAttributes(typeof(DisablePropertyInjectionAttribute), true)
                .IsNullOrEmpty()
        )
        {
            registrationBuilder = registrationBuilder.PropertiesAutowired(
                new ZPropertySelector(false)
            );
        }

        return registrationBuilder;
    }

    private static IRegistrationBuilder<
        TLimit,
        TActivatorData,
        TRegistrationStyle
    > InvokeRegistrationActions<TLimit, TActivatorData, TRegistrationStyle>(
        this IRegistrationBuilder<TLimit, TActivatorData, TRegistrationStyle> registrationBuilder,
        ServiceRegistrationActionList registrationActionList,
        Type serviceType,
        Type implementationType
    )
        where TActivatorData : ReflectionActivatorData
    {
        var serviceRegistredArgs = new OnServiceRegistredContext(serviceType, implementationType);

        foreach (var registrationAction in registrationActionList)
        {
            registrationAction.Invoke(serviceRegistredArgs);
        }

        if (serviceRegistredArgs.Interceptors.Any())
        {
            registrationBuilder = registrationBuilder.AddInterceptors(
                registrationActionList,
                serviceType,
                serviceRegistredArgs.Interceptors
            );
        }

        return registrationBuilder;
    }

    private static IRegistrationBuilder<TLimit, TActivatorData, TRegistrationStyle> AddInterceptors<
        TLimit,
        TActivatorData,
        TRegistrationStyle
    >(
        this IRegistrationBuilder<TLimit, TActivatorData, TRegistrationStyle> registrationBuilder,
        ServiceRegistrationActionList serviceRegistrationActionList,
        Type serviceType,
        IEnumerable<Type> interceptors
    )
        where TActivatorData : ReflectionActivatorData
    {
        if (serviceType.IsInterface)
        {
            registrationBuilder = registrationBuilder.EnableInterfaceInterceptors();
        }
        else
        {
            if (serviceRegistrationActionList.IsClassInterceptorsDisabled)
            {
                return registrationBuilder;
            }
            (
                registrationBuilder
                as IRegistrationBuilder<TLimit, ConcreteReflectionActivatorData, TRegistrationStyle>
            )?.EnableClassInterceptors();
        }

        foreach (var interceptor in interceptors)
        {
            registrationBuilder.InterceptedBy(
                typeof(ZAsyncDeterminationInterceptor<>).MakeGenericType(interceptor)
            );
        }

        return registrationBuilder;
    }
}
